
Rem
bbdoc: LUA Scripting System. Uses LuaJIT 2.0, which is added to LuGI.core.
about: Scripts are stored as strings in the system.

Your LUA script must contain the following functions:
1) Added(entity) - Is called when the entity is added to the system. Use this to setup your entity.
2) Process(entity) - Is called when the entity is processed.
3) Removed(entity) - Is called when the entity leaves the system.

Required component: TScriptComponent.
endrem
Type TScriptSystem Extends TEntityProcessingSystem

	Field _scriptMapper:TComponentMapper
	Field _luaState:Byte ptr
	Field _game:TGame
	
	'scripts, as string, by entity id.
	Field _scripts:TBag
	
	
	'the current script error.
	'displayed in red at 0,0 by TEngine.
	Field _currentScriptError:String

	
	Method Initialize()
		_scriptMapper = TComponentMapper.Create(New TScriptComponent, _world)
		RegisterComponent(New TScriptComponent)
		
		_scripts = New TBag
		
		'create the LUA vm
		_luaState = luaL_newstate()
		If _luaState = Null Then RuntimeError("Could not create LUA state.")
		
		'load default LUA libraries
		luaL_openlibs(_luaState)
		
		InitLuGI(_luaState)
		
		'set Artemax interface global fields.
		TArtemaxInterface._world = _world
		TArtemaxInterface._luaState = _luaState
		TArtemaxInterface._game = _game
		
	End Method
	
	
	'used by TGame to display lua errors.
	Method GetCurrentError:String()
		Return _currentScriptError
	End Method	
	
	
	'executes the Process(entity) function in the LUA script for this entity.
	Method ProcessEntity(e:TEntity)
		Local badscript:Int = RunScriptString(e.GetId())
		If Not badscript
			lua_getglobal(_luaState, "Process")
			If lua_type(_luaState, -1) = LUA_TFUNCTION Then
				lua_pushbmaxobject(_luaState, e)
				If lua_pcall(_luaState, 1, 1, 0) Then
					Local err:String = lua_tostring(_luaState, -1)
					lua_pop(_luaState, 1)
					RuntimeError(err)
				Else
					lua_pop(_luaState, 1)
				End If
			EndIf
		EndIf
	End Method
	

	'loads the script from disk for this entity as a string
	'and stores it in the bag, by entity id.
	'also executes the Added() function in the LUA script
	'if script is valid
	Method Added(e:TEntity)
		Local c:TScriptComponent = TScriptComponent(_scriptMapper.Get(e))
		Local s:String = LoadString(c.GetScriptName())
		If s = "" Then RuntimeError("Could not load script: " + c.GetScriptName())
		
		_scripts.Set(e.GetId(), s)
		Local badscript:Int = RunScriptString(e.GetId())
		If Not badscript
			lua_getglobal(_luaState, "Added")
			If lua_type(_luaState, -1) = LUA_TFUNCTION Then
				lua_pushbmaxobject(_luaState, e)
				If lua_pcall(_luaState, 1, 1, 0) Then
					Local err:String = lua_tostring(_luaState, -1)
					lua_pop(_luaState, 1)
					RuntimeError(err)
				Else
					lua_pop(_luaState, 1)
				End If
			EndIf
		EndIf
	End Method
	
	
	Method Removed(e:TEntity)
		Local badscript:Int = RunScriptString(e.GetId())
		If Not badscript
			lua_getglobal(_luaState, "Removed")
			If lua_type(_luaState, -1) = LUA_TFUNCTION Then
				lua_pushbmaxobject(_luaState, e)
				If lua_pcall(_luaState, 1, 1, 0) Then
					Local err:String = lua_tostring(_luaState, -1)
					lua_pop(_luaState, 1)
					RuntimeError(err)
				Else
					lua_pop(_luaState, 1)
					
					'clear out the script.
					_scripts.Set(e.GetId(), Null)
				End If
			EndIf
		EndIf
	End Method
	
		
	rem
	bbdoc: Refresh the script for passed entity.
	about: Also runs the Added function in the script.
	endrem
	Method RefreshScript(e:TEntity)
		Added(e)
	End Method
	
	
	rem
	bbdoc: Refreshes scripts for all active entities.
	about: Also runs the Added function in the scripts.
	endrem
	Method RefreshAllScripts()
		For Local index:Int = 0 To _actives.GetSize() - 1
			If TEntity(_actives.Get(index))
				Added(TEntity(_actives.Get(index)))
			EndIf
		Next
	End Method

	
	'runs script at specified index. (= entity id)
	'returns true if script is bad
	Method RunScriptString:Int(index:Int)
		Local badscript:Int = False
		Local script:String = String(_scripts.Get(index))
		
		If luaL_dostring(_luaState, script) Then
			badscript = True
			
			'do not override other error
			If _currentScriptError = ""
				_currentScriptError = lua_tostring(_luaState, -1)
			EndIf
			lua_pop(_luaState, 1)
		Else
			badscript = False
			_currentScriptError = ""
		End If
		Return badscript
	End Method

		
	rem
	bbdoc: Runs a script from a file.
	Returns: True if the script is bad.
	endrem
	Method RunScriptFile:Int(fileName:String)
		Local badscript:Int = False
		If luaL_dofile(_luaState, fileName) Then
			badscript = True
			If _currentScriptError = ""
				_currentScriptError = lua_tostring(_luaState, -1)
			EndIf
			lua_pop(_luaState, 1)
		Else
			badscript = False
			_currentScriptError = ""
		End If
		Return badscript
	End Method
	
	
	rem
	bbdoc: Calls a LUA function.
	about: No args! :(
	endrem
	Method CallFunction(functionName:String)
		lua_getglobal(_luaState, functionName)
		If lua_type(_luaState, -1) = LUA_TFUNCTION Then
			If lua_pcall(_luaState, 0, 1, 0) Then
				Local err:String = lua_tostring(_luaState, -1)
				lua_pop(_luaState, 1)
				RuntimeError(err)
			Else
				lua_pop(_luaState, 1)
			End If
		EndIf
	End Method
	

	rem
	bbdoc: Returns LUA global as Int.
	endrem
	Method GetLUAInt:Int(globalName:String)
		Local val:Int = 0
		lua_getglobal(_luaState, globalName)
		If lua_isnumber(_luaState, -1)
			val = lua_tointeger(_luaState, -1)
		EndIf
		lua_pop(_luaState, 1)
		Return val
	End Method
	
	
	Method GetLuaFloat:Float(globalName:String)
		Local val:Float = 0.0
		lua_getglobal(_luaState, globalName)
		If lua_isnumber(_luaState, -1)
			val = lua_tonumber(_luaState, -1)
		EndIf
		lua_pop(_luaState, 1)
		Return val
	End Method
	

	rem
	bbdoc: Returns LUA string global.
	endrem
	Method GetLUAString:String(globalName:String)
		Local val:String = ""
		lua_getglobal(_luaState, globalName)
		If lua_isstring(_luaState, -1)
			val = lua_tostring(_luaState, -1)
		End If
		lua_pop(_luaState, 1)
		Return val
	End Method
			
	
	
	Method SetGame(g:TGame)
		_game = g
	End Method

	
	Method StopState()
		lua_close(_luaState)
	End Method
		
End Type
